#include <string.h>

#include <util/msg.h>
#include <util/list.h>
#include <util/common_macros.h>
#include <util/mem.h>

#include "object.h"

/* ========================================================================== */

static List * class_list = NULL;
static long int obj_count;

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="Inherent ObjectClass Methods">
static boolean __object_destroy(ImpObject * object) {
	object->klass = NULL;
	return TRUE;
}
static boolean __object_clone(ImpObject * clone, ImpObject * object) {
	memcpy(clone, object, object->klass->info.object_size);
	clone->reserved = NULL;
	return TRUE;
}
static boolean __object_write(ImpObject * object, FILE * file) {
	return TRUE;
}
static boolean __object_read(ImpObject ** object, FILE * file) {
	return TRUE;
}
static boolean __object_attribures(ImpObject * object, ImpObject * attrib) {
	return TRUE;
}
// </editor-fold>

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="ObjectClass and Object initialization">
static void __object_class_init(ImpObjectClass * klass) {
	klass->object_clone = __object_clone;
	klass->object_destroy = __object_destroy;
	klass->write = __object_write;
	klass->read = __object_read;
	klass->object_attributes = __object_attribures;
}
static void __object_init(ImpObject * object) {
	object->reserved = NULL;
	object->flags = 0;
	object->id = obj_count;
	object->user_ptr = NULL;
	obj_count++;
}
// </editor-fold>

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="Object and Object Class Invocation Methods">
static void __invoque_object_class_init(ImpObjectClass * klass, ImpObjectClass * parent_class) {
	if (parent_class) {
		__invoque_object_class_init(klass, parent_class->parent);
		if (parent_class->info.class_init_func)
			(*parent_class->info.class_init_func) (klass);
	}
}
static void __invoque_object_init(ImpObject * object, ImpObjectClass * klass) {
	ImpObjectClass * parent_class;

	_verify(object);
	_verify(klass);

	parent_class = klass->parent;
	if (parent_class)
		__invoque_object_init(object, parent_class);
	if (klass->info.object_init_func)
		(*klass->info.object_init_func) (object);
}
static void __invoque_destroy(ImpObject * object, ImpObjectClass * klass) {
	ImpObjectClass * parent;

	if (klass) {

		if (klass->object_destroy) {
			(*klass->object_destroy)(object);
		} else {
			msg_warning("Class %s not implements a destroyer method!\n", klass->info.name);
		}
		parent = klass->parent;
		__invoque_destroy(object, parent);
	}
}
// </editor-fold>
/* ========================================================================== */

ImpObjectClass * imp_object_get_class(void) {
	static ImpObjectClass * klass = NULL;

	if (klass == NULL) {
		ImpObjectClassInfo object_info = {
			"ImpObject",
			sizeof (ImpObject),
			sizeof (ImpObjectClass),
			(ImpObjectClassInitFunc) __object_class_init,
			(ImpObjectInitFunc) __object_init,
			(ImpArgSetFunc) NULL,
			(ImpArgGetFunc) NULL
		};
		klass = imp_object_class_new(
				NULL, /* Parent Class */
				&object_info /* New Object Info */
				);
	}
	return klass;
}
ImpObjectClass * imp_object_class_new(ImpObjectClass * parent_class, ImpObjectClassInfo * info) {

	ImpObjectClass * klass;

	_exit_if_fail(info);

	if (parent_class != NULL) {
		_exit_if_fail(parent_class->info.class_size <= info->class_size);
		_exit_if_fail(parent_class->info.object_size <= info->object_size);
	}

	klass = mem_alloc_size(info->class_size);

	klass->info = *info;
	klass->parent = parent_class;
	__invoque_object_class_init(klass, klass);

	/* Class registration */
	if (!class_list) {
		class_list = list_new();
	}
	list_insert(class_list, klass);

	return klass;
}
ImpObject * imp_object_new(ImpObjectClass * klass) {

	ImpObject * object;

	object = mem_alloc_size(klass->info.object_size);
	object->klass = klass;

	__invoque_object_init(object, klass);

	return object;
}
void imp_object_clone(ImpObject *clone, ImpObject * object) {

	_return_if_fail(clone->klass == object->klass);

	object->klass->object_clone(clone, object);
}
void imp_object_destroy(ImpObject * object) {

	if (IMP_OBJECT_DESTROYED(object)) return;

	IMP_OBJECT_SET_FLAGS(object, IMP_DESTROYED);
	__invoque_destroy(object, object->klass);

	/*
		_return_if_fail (object->klass->object_destroy );

		IMP_OBJECT_SET_FLAGS (object, IMP_DESTROYED);
		(* object->klass->object_destroy) (object);
	 */
}
void imp_object_delete_function(ImpObject * object) {

	if (!object) {
		//object = NULL;
		return;
	}

	_return_if_fail(object);
	if (!IMP_OBJECT_DESTROYED(object)) imp_object_destroy(object);
	mem_free(object);
}

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="Object and Class Casts">
int __compare(void * data, void * arg) {
	ImpObjectClass * klass;
	klass = IMP_OBJECT_CLASS(data);
	return strcmp(klass->info.name, (char *) arg);
}
ImpObjectClass * imp_object_get_class_from_name(char * class_name) {
	if (class_list) list_get_data(class_list, __compare, (void *) class_name);
	return NULL;
}
void * imp_object_class_is_from_class(void * klass, void * from) {

	ImpObjectClass * c;

	_verify(klass);
	_verify(from);

	c = (ImpObjectClass *) klass;

	while (c) {
		if (c == from)
			return klass;
		c = c->parent;
	}

	return NULL;
}
void * imp_object_is_from_class(void * object, void * klass) {
	ImpObjectClass * c;

	_verify(klass);

	if (object == NULL) return NULL;

	c = ((ImpObject *) object)->klass;

	_verify(c);

	while (c) {
		if (c == klass)
			return object;
		c = c->parent;
	}

	return NULL;
}
void * imp_object_class_check_cast(void * klass, void * from) {
	if (!klass) {
		msg_warning("invalid cast from (NULL) pointer to `%s'", IMP_OBJECT_CLASS(from)->info.name);
		return klass;
	}
	if (!imp_object_class_is_from_class(klass, from)) {
		msg_warning("invalid cast from `%s' to `%s'", IMP_OBJECT_CLASS(klass)->info.name, IMP_OBJECT_CLASS(from)->info.name);
		return klass;
	}
	return klass;
}
void * imp_object_check_cast(void * object, void * klass) {
	if (!object) {
		msg_warning("invalid cast from (NULL) pointer to `%s'", IMP_OBJECT_CLASS(klass)->info.name);
		return object;
	}

	if (!((ImpObject *) object)->klass) {
		msg_warning("invalid unclassed pointer in cast to `%s'", IMP_OBJECT_CLASS(klass)->info.name);
		return object;
	}
	if (!imp_object_is_from_class(object, klass)) {
		msg_warning("invalid cast from `%s' to `%s'", ((ImpObject *) object)->klass->info.name, IMP_OBJECT_CLASS(klass)->info.name);
		return object;
	}
	return object;
}

// </editor-fold>

/* ========================================================================== */